package com.fast.develop.meta.base.util;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import sun.net.www.protocol.file.FileURLConnection;

import java.io.*;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import static cn.hutool.core.io.file.PathUtil.walkFiles;

/**
 * @author 莫寅
 * @createTime 2022-06-26 00:51
 * @desc：
 */
@Slf4j
public class FileUtil {


    /**
     * 递归遍历目录以及子目录中的所有文件<br>
     * 如果提供path为文件，直接返回过滤结果
     *
     * @param path       当前遍历文件或目录
     * @param pathFilter 文件过滤规则对象，选择要保留的文件，只对文件有效，不过滤目录，null表示接收全部文件
     * @return 文件列表
     * @since 5.4.1
     */
    public static Map<String, File> loopFolderFiles(String path, PathFilter pathFilter) {
        final Map<String, File> fileList = new HashMap<>();

        File file = cn.hutool.core.io.FileUtil.file(path);

        if (null == path || false == file.exists()) {
            return fileList;
        } else if (false == file.isDirectory()) {
            if (null == pathFilter || pathFilter.accept(file.getPath())) {
                fileList.put(file.getPath(), file);
            }
            return fileList;
        }

        walkFiles(file.toPath(), -1, new SimpleFileVisitor<Path>() {

            @Override
            public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
                final File file = path.toFile();
                if (null == pathFilter || pathFilter.accept(file.getPath())) {
                    fileList.put(file.getPath(), file);
                }
                return FileVisitResult.CONTINUE;
            }
        });

        return fileList;
    }


    @SneakyThrows
    public static Map<String, InputStream> loopFiles(String folder, PathFilter pathFilter) {
        Map<String, InputStream> files = new HashMap<>();

        URL url = FileUtil.class.getClassLoader().getResource(folder);
        boolean isFolder = cn.hutool.core.io.FileUtil.isDirectory(folder);

        if(url == null && !isFolder){
            return MapUtil.empty();
        }

        URLConnection urlConnection = null;
        if(url != null){
            urlConnection = url.openConnection();
        }

        if(isFolder){
            setFolderFiles(folder, files, pathFilter);
        }else if(url != null && urlConnection instanceof FileURLConnection){
            setFolderFiles(folder, files, pathFilter);
        } else if (urlConnection instanceof JarURLConnection) {
            JarURLConnection jarURLConnection = (JarURLConnection) urlConnection;
            setJarURLConnectionFiles(jarURLConnection, files, pathFilter);
        }

        return files;
    }

    private static void setFolderFiles(String folder, Map<String, InputStream> files, PathFilter pathFilter){
        loopFolderFiles(folder, pathFilter).entrySet().stream().forEach(stringFileEntry -> {
            files.put(stringFileEntry.getKey(), createInputStream(stringFileEntry.getValue()));
        });
    }

    @SneakyThrows
    private static void setJarURLConnectionFiles(JarURLConnection jarURLConnection, Map<String, InputStream> files, PathFilter pathFilter){
        JarFile jarFile = jarURLConnection.getJarFile();
        Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
            JarEntry entry = entries.nextElement();
            if (entry.getName().startsWith(jarURLConnection.getEntryName()) && !entry.getName().endsWith("/")) {
                if(pathFilter != null && pathFilter.accept(entry.getName())){
                    files.put(entry.getName(), FileUtil.class.getResourceAsStream(StrUtil.SLASH + entry.getName()));
                }else{
                    files.put(entry.getName(), FileUtil.class.getResourceAsStream(StrUtil.SLASH + entry.getName()));
                }
            }
        }
    }

    @SneakyThrows
    private static InputStream createInputStream(File file){
        return new FileInputStream(file);
    }
}
